/*
 * Copyright (c) 2008-2018, RF-Embedded GmbH
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 *  1. Redistributions of source code must retain the above copyright notice,
 *     this list of conditions and the following disclaimer.
 *  2. Redistributions in binary form must reproduce the above copyright notice,
 *     this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
 * SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "QrfeProtocolHandler.h"
#include "../QrfeReaderInterface.h"

#include <QMetaType>

using namespace QrfeReaderInterface;

/**
 * @brief QrfeProtocolHandler::QrfeProtocolHandler constructs a new protocol handler
 * @param device    The communication device
 * @param parent    Parent object
 */
QrfeProtocolHandler::QrfeProtocolHandler(QIODevice *device, QObject* parent )
    : QThread(parent)
{
    m_responseTimeOut = 1000;
    m_pendingResponseTimeOut = 10000;
    m_lastReturnCode = RFE_RET_SUCCESS;
    m_blockCyclicInventoryInterrupts = false;

    m_device = device;

    m_state = START_BYTE_1;
    m_payloadIndex = 0;
    m_payloadLength = 0;

    m_threadInitialized = false;
    this->start(QThread::HighestPriority);

    while(!m_threadInitialized){
        msleep(10);
    }

    if(this != QThread::currentThread ())
        m_device->moveToThread(this);
}

/**
 * @brief QrfeProtocolHandler::~QrfeProtocolHandler destructs the protocol handler
 */
QrfeProtocolHandler::~QrfeProtocolHandler()
{
    quit();
    while(!isFinished ())
        sleep(1);
}

/**
 * @brief QrfeProtocolHandler::getResponseTimeout gets the specified response time
 * @return The specified response time
 */
int  QrfeProtocolHandler::getResponseTimeout()
{
    return m_responseTimeOut;
}

/**
 * @brief QrfeProtocolHandler::setResponseTimeout sets the specified response time
 * @param responseTimeout   The desired response time
 */
void QrfeProtocolHandler::setResponseTimeout(int responseTimeout)
{
    m_responseTimeOut = responseTimeout;
}

/**
 * @brief QrfeProtocolHandler::getLastReturnCode returns the last return code from the reader
 * @return Last return code
 */
eRFE_RET_VALUE QrfeProtocolHandler::getLastReturnCode()
{
    return m_lastReturnCode;
}

/**
 * @brief QrfeProtocolHandler::getBlockCyclicInventoryInterrupts returns if the cyclic inventory interrupts are blocked.
 * @return Returns if the cyclic inventory interrupts are blocked.
 */
bool QrfeProtocolHandler::getBlockCyclicInventoryInterrupts()
{
    return m_blockCyclicInventoryInterrupts;
}

/**
 * @brief QrfeProtocolHandler::setBlockCyclicInventoryInterrupts sets the cyclic inventory interrupts blocked
 * @param block     Specifies if the interrupts should be blocked
 */
void QrfeProtocolHandler::setBlockCyclicInventoryInterrupts(bool block)
{
    m_blockCyclicInventoryInterrupts = block;
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

/**
 * @brief QrfeProtocolHandler::getReaderID retrieves the reader ID of the reader.
 * @param readerID  Retrieved reader ID
 * @return Success of the operation
 */
bool QrfeProtocolHandler::getReaderID(ulong &readerID)
{
    Global::trc(1, "Get Reader ID - Trying to get Reader ID");
    uchar com1 = RFE_COM1_READER_COMMON;
    uchar com2 = RFE_COM2_GET_SERIAL_NUMBER;

    QByteArray response;
    bool res = send2ReaderWaitForResponse(com1, com2, m_responseTimeOut, "Get Reader ID", response);

    if(res != true)
        return res;

    if (response.size() != 4){
        Global::trc(0, "Get Reader ID - NOK - Payl");
        Global::trc(0, " Recvd: " + response.toHex());
        return false;
    }

    readerID = 0;
    readerID |= (((ulong) (uchar) response.at(0)) << 24);
    readerID |= (((ulong) (uchar) response.at(1)) << 16);
    readerID |= (((ulong) (uchar) response.at(2)) << 8);
    readerID |= (ulong) (uchar) response.at(3);

    Global::trc(1, "Get Reader ID - OK : Serial Number = " + QString("%1").arg(readerID, 8, 16,
            QChar('0')));

    return true;
}

/**
 * @brief QrfeProtocolHandler::getReaderType retrieves the reader type of the reader.
 * @param readerType    Retrieved reader type
 * @return Success of the operation
 */
bool QrfeProtocolHandler::getReaderType(ulong &readerType)
{
    Global::trc(1, "Get Reader Type - Trying to get Reader Type");
    uchar com1 = RFE_COM1_READER_COMMON;
    uchar com2 = RFE_COM2_GET_READER_TYPE;

    QByteArray response;
    bool res = send2ReaderWaitForResponse(com1, com2, m_responseTimeOut, "Get Reader Type", response);

    if(res != true)
        return res;

    if (response.size() != 4){
        Global::trc(0, "Get Reader Type - NOK - Payl");
        Global::trc(0, " Recvd: " + response.toHex());
        return false;
    }

    readerType = 0;
    readerType |= (((ulong) (uchar) response.at(0)) << 24);
    readerType |= (((ulong) (uchar) response.at(1)) << 16);
    readerType |= (((ulong) (uchar) response.at(2)) << 8);
    readerType |= (ulong) (uchar) response.at(3);

    Global::trc(1, "Get Reader Type - OK : Reader Type = " + QString("%1").arg(readerType, 8, 16, QChar('0')));

    return true;
}

/**
 * @brief QrfeProtocolHandler::getHardwareRevision retrieves the hardware revision of the reader.
 * @param hardwareRevision  Retrieved hardware revision
 * @return Success of the operation
 */
bool QrfeProtocolHandler::getHardwareRevision(
        ulong &hardwareRevision)
{
    Global::trc(1, "Get Hardware Rev - Trying to get hardware ID");
    uchar com1 = RFE_COM1_READER_COMMON;
    uchar com2 = RFE_COM2_GET_HARDWARE_REVISION;

    QByteArray response;
    bool res = send2ReaderWaitForResponse(com1, com2, m_responseTimeOut, "Get Hardware Revision", response);

    if(res != true)
        return res;

    if (response.size() == 0){
        Global::trc(0, "Get Hardware Rev - NOK - Payl");
        Global::trc(0, " Recvd: " + response.toHex());
        return false;
    }

    // set the variable to the new value

    hardwareRevision = 0;
    hardwareRevision |= (((ulong) (uchar) response.at(0)) << 24);
    hardwareRevision |= (((ulong) (uchar) response.at(1)) << 16);
    hardwareRevision |= (((ulong) (uchar) response.at(2)) << 8);
    hardwareRevision |= (ulong) (uchar) response.at(3);

    Global::trc(1, "Get Hardware Rev - OK : Hardware Revision = " + QString("%1").arg(hardwareRevision, 8, 16, QChar('0')));

    return true;
}

/**
 * @brief QrfeProtocolHandler::getSoftwareRevision retrieves the software revision of the reader.
 * @param softwareRevision  Retrieved software revision
 * @return Success of the operation
 */
bool QrfeProtocolHandler::getSoftwareRevision(
        ulong &softwareRevision)
{
    Global::trc(1, "Get Software Rev - Trying to get software ID");
    uchar com1 = RFE_COM1_READER_COMMON;
    uchar com2 = RFE_COM2_GET_SOFTWARE_REVISION;

    QByteArray response;
    bool res = send2ReaderWaitForResponse(com1, com2, m_responseTimeOut, "Get Software Revision", response);

    if(res != true)
        return res;

    if (response.size() == 0){
        Global::trc(0, "Get Software Rev - NOK - Payl");
        Global::trc(0, " Recvd: " + response.toHex());

        return false;
    }

    // set the variable to the new value
    softwareRevision = 0;
    softwareRevision |= (((ulong) (uchar) response.at(0)) << 24);
    softwareRevision |= (((ulong) (uchar) response.at(1)) << 16);
    softwareRevision |= (((ulong) (uchar) response.at(2)) << 8);
    softwareRevision |= (ulong) (uchar) response.at(3);

    Global::trc(1, "Get Software Rev - OK : Software Revision = " + QString("%1").arg(softwareRevision, 8, 16, QChar('0')));

    return true;
}

/**
 * @brief QrfeProtocolHandler::getBootloaderRevision retrieves the bootloader revision of the reader.
 * @param bootloaderRevision    Retrieved bootloader revision
 * @return Success of the operation
 */
bool QrfeProtocolHandler::getBootloaderRevision(
        ulong &bootloaderRevision)
{
    Global::trc(1, "Get Bootloader Rev - Trying to get bootloader ID");
    uchar com1 = RFE_COM1_READER_COMMON;
    uchar com2 = RFE_COM2_GET_BOOTLOADER_REVISION;

    QByteArray response;
    bool res = send2ReaderWaitForResponse(com1, com2, m_responseTimeOut, "Get Bootloader Revision", response);

    if(res != true)
        return res;

    if (response.size() != 4){
        Global::trc(0, "Get Bootloader Rev - NOK - Payl");
        Global::trc(0, " Recvd: " + response.toHex());

        return false;
    }

    // set the variable to the new value
    bootloaderRevision = 0;
    bootloaderRevision |= (((ulong) (uchar) response.at(0)) << 24);
    bootloaderRevision |= (((ulong) (uchar) response.at(1)) << 16);
    bootloaderRevision |= (((ulong) (uchar) response.at(2)) << 8);
    bootloaderRevision |= (ulong) (uchar) response.at(3);

    Global::trc(1, "Get Bootloader Rev - OK : Bootloader Revision = " + QString("%1").arg(bootloaderRevision, 8, 16, QChar('0')));

    return true;
}

/**
 * @brief QrfeProtocolHandler::getCurrentSystem retrieves the current running system of the reader.
 * @param current   System The current running sytem of the reader
 * @return Success of the operation
 */
bool QrfeProtocolHandler::getCurrentSystem(
        QString &currentSystem)
{
    Global::trc(1, "Get Current System - Trying to get current System");
    uchar com1 = RFE_COM1_READER_COMMON;
    uchar com2 = RFE_COM2_GET_CURRENT_SYSTEM;

    QByteArray response;
    bool res = send2ReaderWaitForResponse(com1, com2, m_responseTimeOut, "Get Current System", response);

    if(res != true)
        return res;

    if (response.size() != 1){
        Global::trc(1, "Get Current System - NOK - Payl");
        Global::trc(0, " Recvd: " + response.toHex());

        return false;
    }

    if ((uchar) response.at(0) == FIRMWARE)
    {
        currentSystem = "Firmware";
    }
    else if ((uchar) response.at(0) == BOOTLOADER)
    {
        currentSystem = "Bootloader";
    }
    else
    {
        Global::trc(0, "Get Current System - NOK - Payl");
                Global::trc(0, " Recvd: " + response.toHex());

        return false;
    }

    Global::trc(1, "Get Current System - OK : System " + currentSystem);

    return true;
}

/**
 * @brief QrfeProtocolHandler::getCurrentState retrieves the current state of the reader.
 * @param current   State Current state of the reader
 * @return Success of the operation
 */
bool QrfeProtocolHandler::getCurrentState(
        eRFE_CURRENT_READER_STATE &currentState)
{
    Global::trc(1, "Get Current State - Trying to get current State");
    uchar com1 = RFE_COM1_READER_COMMON;
    uchar com2 = RFE_COM2_GET_CURRENT_STATE;

    QByteArray response;
    bool res = send2ReaderWaitForResponse(com1, com2, m_responseTimeOut, "Get Current State", response);

    if(res != true)
        return res;

    if (response.size() != 1){
        Global::trc(1, "Get Current State - NOK - Payl");
        Global::trc(0, " Recvd: " + response.toHex());

        return false;
    }

    if ((uchar) response.at(0) == RFE_STATE_IDLE)
    {
        currentState = RFE_STATE_IDLE;
    }
    else if ((uchar) response.at(0) == RFE_STATE_SCANNING)
    {
        currentState = RFE_STATE_SCANNING;
    }
    else if ((uchar) response.at(0) == RFE_STATE_REBOOTING)
    {
        currentState = RFE_STATE_REBOOTING;
    }
    else
    {
        Global::trc(0, "Get Current State - NOK - Payl");
        Global::trc(0, " Recvd: " + response.toHex());

        return false;
    }

    Global::trc(1, "Get Current State - OK : State " + QString::number(currentState));

    return true;
}

/**
 * @brief QrfeProtocolHandler::getStatusRegister retrieves the status register of the reader.
 * @param statusRegister    Status register of the reader
 * @return Success of the operation
 */
bool QrfeProtocolHandler::getStatusRegister ( qulonglong &statusRegister )
{
    Global::trc(1, "Get Status Register - Trying to get current State");
    uchar com1 = RFE_COM1_READER_COMMON;
    uchar com2 = RFE_COM2_GET_STATUS_REGISTER;

    QByteArray response;
    bool res = send2ReaderWaitForResponse(com1, com2, m_responseTimeOut, "Get Status Register", response);

    if(res != true)
        return res;

    if (response.size() != 8){
        Global::trc(0, "Get Status Register - NOK - Payl");
        Global::trc(0, " Recvd: " + response.toHex());

        return false;
    }

    statusRegister = 0;
    statusRegister |= (((qulonglong) (uchar) response.at(0)) << 56);
    statusRegister |= (((qulonglong) (uchar) response.at(1)) << 48);
    statusRegister |= (((qulonglong) (uchar) response.at(2)) << 40);
    statusRegister |= (((qulonglong) (uchar) response.at(3)) << 32);
    statusRegister |= (((qulonglong) (uchar) response.at(4)) << 24);
    statusRegister |= (((qulonglong) (uchar) response.at(5)) << 16);
    statusRegister |= (((qulonglong) (uchar) response.at(6)) << 8);
    statusRegister |= (qulonglong) (uchar) response.at(7);

    Global::trc(1, "Get Status Register - OK : Status Register = " + QString("%1").arg(statusRegister, 16, 16, QChar('0')));

    return true;
}

/**
 * @brief QrfeProtocolHandler::getAntennaCount retrieves the antenna count of the reader.
 * @param count     Antenna count of the reader
 * @return Success of the operation
 */
bool QrfeProtocolHandler::getAntennaCount ( uchar &count )
{
    Global::trc(1, "Get Antenna Count - Trying to get Antenna Count");
    uchar com1 = RFE_COM1_READER_COMMON;
    uchar com2 = RFE_COM2_GET_ANTENNA_COUNT;

    QByteArray response;
    bool res = send2ReaderWaitForResponse(com1, com2, m_responseTimeOut, "Get Antenna Count", response);

    if(res != true)
        return res;

    if (response.size() != 2 || response.at(0) != RFE_RET_SUCCESS){
        Global::trc(0, "Get Antenna Count - NOK - Payl");
        Global::trc(0, " Recvd: " + response.toHex());

        return false;
    }

    count = (uchar) response.at(1);

    Global::trc(1, "Get Antenna Count - OK : Count = " + QString("%1").arg(count));

    return true;
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

/**
 * @brief QrfeProtocolHandler::getAttenuation retrieves the attenuation settings of the reader.
 * @param maxOutputPower        Maximum settable value for the attenuation
 * @param currentOutputPower    Current set value for the attenuation
 * @return Success of the operation
 */
bool QrfeProtocolHandler::getAttenuation(ushort &maxOutputPower, ushort &currentOutputPower)
{
    Global::trc(1, "Get Attenuation - Trying to attenuation");
    uchar com1 = RFE_COM1_READER_RF;
    uchar com2 = RFE_COM2_GET_ATTENUATION;

    QByteArray response;
    bool res = send2ReaderWaitForResponse(com1, com2, m_responseTimeOut, "Get Attenuatuin", response);

    if(res != true)
        return res;

    if (response.size() != 5 || response.at(0) != RFE_RET_SUCCESS)
    {
        Global::trc(0, "Get Attenuation - NOK - Payl");
        Global::trc(0, " Recvd: " + response.toHex());

        m_lastReturnCode = (eRFE_RET_VALUE) (uchar) response.at(0);
        return false;
    }

    // set the variable to the new value
    maxOutputPower = 0;
    maxOutputPower |= (((ushort) (uchar) response.at(1)) << 8);
    maxOutputPower |= ((ushort) (uchar) response.at(2));

    currentOutputPower = 0;
    currentOutputPower |= (((ushort) (uchar) response.at(3)) << 8);
    currentOutputPower |= (ushort) (uchar) response.at(4);

    Global::trc(1, "Get Attenuation - OK : Maximum Attenuation " + QString::number(maxOutputPower) +
                                 " Current Attenuation " + QString::number(currentOutputPower));

    return true;
}

/**
 * @brief QrfeProtocolHandler::getFrequency retrieves the frequency table and the current set mode of the reader.
 * @param mode                  Current used mode
 * @param maxFrequencyCount     Maximum count of frequency table entries
 * @param frequencies           Frequency table
 * @return Success of the operation
 */
bool QrfeProtocolHandler::getFrequency(uchar &mode, uchar &maxFrequencyCount, QList<uint> &frequencies)
{
    Global::trc(1, "Get Frequency - Trying to frequency");
    uchar com1 = RFE_COM1_READER_RF;
    uchar com2 = RFE_COM2_GET_FREQUENCY;

    QByteArray response;
    bool res = send2ReaderWaitForResponse(com1, com2, m_responseTimeOut, "Get Frequency", response);

    if(res != true)
        return res;

    if (response.size() < 5 || response.at(0)
            != RFE_RET_SUCCESS)
    {
        Global::trc(0, "Get Frequency - NOK - Payl");
        Global::trc(0, " Recvd: " + response.toHex());

        m_lastReturnCode = (eRFE_RET_VALUE) (uchar) response.at(0);
        return false;
    }

    mode = response.at(1);
    maxFrequencyCount = response.at(2);
    frequencies.clear();
    uchar count = response.at(3);
    uchar index = 4;
    for (int i = 0; i < count; i++)
    {
        uint freq = 0;
        freq |= (((ulong) (uchar) response.at(index++)) << 16);
        freq |= (((ulong) (uchar) response.at(index++)) << 8);
        freq |= (ulong) (uchar) response.at(index++);

        frequencies.append(freq);
    }

    Global::trc(1, "Get Frequency - OK");

    return true;
}

/**
 * @brief QrfeProtocolHandler::getSensitivity retrieves the sensitivity settings of the reader.
 * @param maxSensitivity        Maximum settable sensitivity
 * @param minSensitivity        Mininim settable sensitivity
 * @param currentSensitivity    Current set sensitivity
 * @return Success of the operation
 */
bool QrfeProtocolHandler::getSensitivity( short &maxSensitivity, short &minSensitivity, short &currentSensitivity )
{
    Global::trc(1, "Get Sensitivity - Trying to get Sensitivity");

    uchar com1 = RFE_COM1_READER_RF;
    uchar com2 = RFE_COM2_GET_SENSITIVITY;

    QByteArray response;
    bool res = send2ReaderWaitForResponse(com1, com2, m_responseTimeOut, "Get Sensitivity", response);

    if(res != true)
        return res;

    if (response.size() != 7 || response.at(0) != RFE_RET_SUCCESS)
    {
        Global::trc(0, "Get Sensitivity - NOK - Payl");
        Global::trc(0, " Recvd: " + response.toHex());

        m_lastReturnCode = (eRFE_RET_VALUE) (uchar) response.at(0);
        return false;
    }

    // set the variable to the new value
    maxSensitivity = 0;
    maxSensitivity |= (((short) (uchar) response.at(1)) << 8);
    maxSensitivity |= (short) (uchar) response.at(2);

    minSensitivity = 0;
    minSensitivity |= (((short) (uchar) response.at(3)) << 8);
    minSensitivity |= (short) (uchar) response.at(4);

    currentSensitivity = 0;
    currentSensitivity |= (((short) (uchar) response.at(5)) << 8);
    currentSensitivity |= (short) (uchar) response.at(6);

    Global::trc(1, "Get Sensitivity - OK : Maximum Sensitivity " + QString::number(maxSensitivity) +
                                 " Minimum Sensitivity " + QString::number(minSensitivity) +
                                 " Current Sensitivity " + QString::number(currentSensitivity));

    return true;
}

/**
 * @brief QrfeProtocolHandler::getLbtParams retrieves the LBT params of the reader.
 * @param listenTime        Listen time in msecs
 * @param idleTime          Idle time in msecs
 * @param maxAllocTime      Maximum allocation time in msecs
 * @param rssiThreshold     RSSI threshold
 * @return Success of the operation
 */
bool QrfeProtocolHandler::getLbtParams ( ushort &listenTime, ushort &idleTime, ushort &maxAllocTime, short &rssiThreshold )
{
    Global::trc(1, "Get LBT-Params - Trying to get LBT Params");

    uchar com1 = RFE_COM1_READER_RF;
    uchar com2 = RFE_COM2_GET_LBT_PARAMS;

    QByteArray response;
    bool res = send2ReaderWaitForResponse(com1, com2, m_responseTimeOut, "Get LBT-Params", response);

    if(res != true)
        return res;

    if (response.size() != 9 || response.at(0) != RFE_RET_SUCCESS)
    {
        Global::trc(0, "Get LBT-Params - NOK - Payl");
        Global::trc(0, " Recvd: " + response.toHex());

        m_lastReturnCode = (eRFE_RET_VALUE) (uchar) response.at(0);
        return false;
    }

    // set the variable to the new value
    listenTime = 0;
    listenTime |= (((ushort) (uchar) response.at(1)) << 8);
    listenTime |= (ushort) (uchar) response.at(2);

    idleTime = 0;
    idleTime |= (((ushort) (uchar) response.at(3)) << 8);
    idleTime |= (ushort) (uchar) response.at(4);

    maxAllocTime = 0;
    maxAllocTime |= (((ushort) (uchar) response.at(5)) << 8);
    maxAllocTime |= (ushort) (uchar) response.at(6);

    rssiThreshold = 0;
    rssiThreshold |= (((short) (uchar) response.at(7)) << 8);
    rssiThreshold |= (short) (uchar) response.at(8);

    Global::trc(1, "Get LBT-Params - OK : Listen Time " + QString::number(listenTime) +
                                 " Idle Time " + QString::number(idleTime) +
                                 " Maximum Allocation Time " + QString::number(maxAllocTime) +
                                 " RSSI Threshold " + QString::number(rssiThreshold));

    return true;

}

/**
 * @brief QrfeProtocolHandler::setAttenuation sets the attenuation setting of the reader
 * @param value The new attenuation value
 * @return Success of the operation
 */
bool QrfeProtocolHandler::setAttenuation(ushort value)
{
    Global::trc(1, "Set Attenuation - Trying to set output power to " + QString::number(value));

    QByteArray payload;
    payload.resize(2);
    payload[0] = (uchar) (value >> 8);
    payload[1] = (uchar) (value);

    uchar com1 = RFE_COM1_READER_RF;
    uchar com2 = RFE_COM2_SET_ATTENUATION;

    QByteArray response;
    bool res = send2ReaderWaitForResponse(com1, com2, payload, m_responseTimeOut, "Set Attenuation", response);

    if(res != true)
        return res;

    // set the variable to the new value
    if (response.size() == 0 || response.at(0) != RFE_RET_SUCCESS)
    {
        Global::trc(0, "Set Attenuation - NOK - Payl");
        Global::trc(0, " Recvd: " + response.toHex());

        m_lastReturnCode = (eRFE_RET_VALUE) (uchar) response.at(0);
        return false;
    }

    Global::trc(1, "Set Attenuation - OK");

    return true;
}


///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

/**
 * @brief QrfeProtocolHandler::reboot reboots the reader
 * @return Success of the operation
 */
bool QrfeProtocolHandler::reboot()
{
    Global::trc(1, "Reboot - Trying to reboot");

    uchar com1 = RFE_COM1_READER_CONTROL;
    uchar com2 = RFE_COM2_REBOOT;

    // send the command
    bool res = send2ReaderWithoutResponse(com1, com2, "Reboot");
    if (res != true){
        Global::trc(0, "Reboot - NOK - Send");
        return res;
    }

    return true;
}

/**
 * @brief QrfeProtocolHandler::setHeartBeat sets the heartbeat settings of the reader
 * @param type      Specifies if the reader should send a heartbeat
 * @param interval  Specifies the interval of the heartbeat
 * @return Success of the operation
 */
bool QrfeProtocolHandler::setHeartBeat ( eRFE_HEARTBEAT_TYPE type, ushort interval )
{
    Global::trc(1, "Set Heartbeat - Trying to set heartbeat to type #" + QString::number(type));

    QByteArray payload;
    payload.resize(1);
    payload[0] = type;
    if (interval != 0)
    {
        payload.resize(3);
        payload[1] = (uchar) (interval >> 8);
        payload[2] = (uchar) (interval);
    }

    uchar com1 = RFE_COM1_READER_CONTROL;
    uchar com2 = RFE_COM2_SET_HEARTBEAT;

    QByteArray response;
    bool res = send2ReaderWaitForResponse(com1, com2, payload, m_responseTimeOut, "Set Heartbeat", response);

    if(res != true)
        return res;

    // set the variable to the new value
    if (response.size() == 0 || response.at(0)
            != RFE_RET_SUCCESS)
    {
        Global::trc(0, "Set Heartbeat - NOK - Payl");
        Global::trc(0, " Recvd: " + response.toHex());

        m_lastReturnCode = (eRFE_RET_VALUE) (uchar) response.at(0);
        return false;
    }

    Global::trc(1, "Set Heartbeat - OK");

    return true;

}

/**
 * @brief QrfeProtocolHandler::setAntennaPower sets the antenna power of the reader
 * @param on    Specifies if the antenna power should be activated
 * @return Success of the operation
 */
bool QrfeProtocolHandler::setAntennaPower(bool on)
{
    Global::trc(1, "Set Antenna - Trying to set antenna power " + QString((on) ? "ON" : "OFF"));

    QByteArray payload;
    payload.resize(1);
    payload[0] = (on) ? ANTENNA_ON : ANTENNA_OFF;

    uchar com1 = RFE_COM1_READER_CONTROL;
    uchar com2 = RFE_COM2_SET_ANTENNA_POWER;

    QByteArray response;
    bool res = send2ReaderWaitForResponse(com1, com2, payload, m_responseTimeOut, "Set Antenna Power", response);

    if(res != true)
        return res;

    // set the variable to the new value
    if (response.size() == 0 || response.at(0) != RFE_RET_SUCCESS)
    {
        Global::trc(0, "Set Antenna - NOK - Payl");
        Global::trc(0, " Recvd: " + response.toHex());

        m_lastReturnCode = (eRFE_RET_VALUE) (uchar) response.at(0);
        return false;
    }

    Global::trc(1, "Set Antenna - OK");

    return true;
}

/**
 * @brief QrfeProtocolHandler::saveSettingsPermanent saves the settings permanent on the reader
 * @return Success of the operation
 */
bool QrfeProtocolHandler::saveSettingsPermanent()
{
    Global::trc(1, "Save Settings - Trying save settings permanent");

    uchar com1 = RFE_COM1_READER_CONTROL;
    uchar com2 = RFE_COM2_SAVE_SETTINGS_PERMANENT;

    QByteArray response;
    bool res = send2ReaderWaitForResponse(com1, com2, m_responseTimeOut, "Save Settings", response);

    if(res != true)
        return res;

    // set the variable to the new value
    if (response.size() == 0 || response.at(0)
            != RFE_RET_SUCCESS)
    {
        Global::trc(0, "Save Settings - NOK - Payl");
        Global::trc(0, " Recvd: " + response.toHex());

        m_lastReturnCode = (eRFE_RET_VALUE) (uchar) response.at(0);
        return false;
    }

    Global::trc(1, "Save Settings - OK");

    return true;

}

/**
 * @brief QrfeProtocolHandler::restoreFactorySettings restores the factory settings of the reader
 * @return Success of the operation
 */
bool QrfeProtocolHandler::restoreFactorySettings()
{
    Global::trc(1, "Restore Settings - Trying to restore settings");
    uchar com1 = RFE_COM1_READER_CONTROL;
    uchar com2 = RFE_COM2_RESTORE_FACTORY_SETTINGS;

    QByteArray response;
    bool res = send2ReaderWaitForResponse(com1, com2, m_responseTimeOut, "Restore Settings", response);

    if(res != true)
        return res;

    // set the variable to the new value
    if (response.size() == 0 || response.at(0)
            != RFE_RET_SUCCESS)
    {
        Global::trc(0, "Restore Settings - NOK - Payl");
        Global::trc(0, " Recvd: " + response.toHex());

        m_lastReturnCode
                = (eRFE_RET_VALUE) (uchar) response.at(0);
        return false;
    }

    Global::trc(1, "Restore Settings - OK");

    return true;
}


/**
 * @brief QrfeProtocolHandler::getParam retrieves a parameter from the reader at the given address
 * @param address   Address of the paremeter
 * @param value     Retrieved value of the parameter
 * @return Success of the operation
 */
bool QrfeProtocolHandler::getParam(ushort address, QByteArray &value)
{
    Global::trc(1, "Get Param - Trying to get param of address " + QString::number(	address));

    QByteArray payload;
    payload.resize(2);
    payload[0] = (uchar) (address >> 8);
    payload[1] = (uchar) (address);

    uchar com1 = RFE_COM1_READER_CONTROL;
    uchar com2 = RFE_COM2_GET_PARAM;

    QByteArray response;
    bool res = send2ReaderWaitForResponse(com1, com2, payload, m_responseTimeOut, "Get Param", response);

    if(res != true)
        return res;

    // set the variable to the new value
    if (response.size() == 0 || response.at(0)
            != RFE_RET_SUCCESS)
    {
        Global::trc(0, "Get Param - NOK - Payl");
        Global::trc(0, " Recvd: " + response.toHex());

        m_lastReturnCode = (eRFE_RET_VALUE) (uchar) response.at(0);
        return false;
    }

    value.clear();
    value.append(response.right(response.size() - 2));

    Global::trc(1, "Get Param - OK : " + value.toHex());

    return true;
}


///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

/**
 * @brief QrfeProtocolHandler::getGPIOCaps retrieves the GPIO capabillities of the reader
 * @param mask      Bit mask of available GPIOs
 * @param output    Bit mask of GPIOs that are available as output
 * @param input     Bit mask of GPIOs that are available as input
 * @return Success of the operation
 */
bool QrfeProtocolHandler::getGPIOCaps ( ulong &mask, ulong &output, ulong &input )
{
    Global::trc(1, "Get GPIO Caps - Trying to get GPIO Caps");

    uchar com1 = RFE_COM1_READER_GPIO;
    uchar com2 = RFE_COM2_GET_GPIO_CAPS;

    QByteArray response;
    bool res = send2ReaderWaitForResponse(com1, com2, m_responseTimeOut, "Get GPIO Caps", response);

    if(res != true)
        return res;

    if (response.size() != 12){
        Global::trc(0, "Get GPIO Caps - NOK - Payl");
        Global::trc(0, " Recvd: " + response.toHex());

        return false;
    }

    mask = 0;
    mask |= (((ulong) (uchar) response.at(0)) << 24);
    mask |= (((ulong) (uchar) response.at(1)) << 16);
    mask |= (((ulong) (uchar) response.at(2)) << 8);
    mask |= (ulong) (uchar) response.at(3);

    output = 0;
    output |= (((ulong) (uchar) response.at(4)) << 24);
    output |= (((ulong) (uchar) response.at(5)) << 16);
    output |= (((ulong) (uchar) response.at(6)) << 8);
    output |= (ulong) (uchar) response.at(7);

    input = 0;
    input |= (((ulong) (uchar) response.at(8)) << 24);
    input |= (((ulong) (uchar) response.at(9)) << 16);
    input |= (((ulong) (uchar) response.at(10)) << 8);
    input |= (ulong) (uchar) response.at(11);

    Global::trc(1, "Get GPIO Caps - OK : Mask   = " + QString("%1").arg(mask, 8, 16, QChar('0')) +
                               " Output = " + QString("%1").arg(output, 8, 16, QChar('0')) +
                               " Input  = " + QString("%1").arg(input, 8, 16, QChar('0')) );

    return true;
}

/**
 * @brief QrfeProtocolHandler::getGPIODirection retrieves the current set GPIO direction
 * @param direction     Bit mask of the current direction, 1 = output, 2 = input
 * @return Success of the operation
 */
bool QrfeProtocolHandler::getGPIODirection ( ulong &direction )
{
    Global::trc(1, "Get GPIO Direction - Trying to get GPIO Direction");

    uchar com1 = RFE_COM1_READER_GPIO;
    uchar com2 = RFE_COM2_GET_GPIO_DIRECTION;

    QByteArray response;
    bool res = send2ReaderWaitForResponse(com1, com2, m_responseTimeOut, "Get GPIO Direction", response);

    if(res != true)
        return res;

    if (response.size() != 4){
        Global::trc(0, "Get GPIO Direction - NOK - Payl");
        Global::trc(0, " Recvd: " + response.toHex());

        return false;
    }

    direction = 0;
    direction |= (((ulong) (uchar) response.at(0)) << 24);
    direction |= (((ulong) (uchar) response.at(1)) << 16);
    direction |= (((ulong) (uchar) response.at(2)) << 8);
    direction |= (ulong) (uchar) response.at(3);

    Global::trc(1, "Get GPIO Direction - OK : Direction = " + QString("%1").arg(direction, 8, 16, QChar('0')) );

    return true;
}

/**
 * @brief QrfeProtocolHandler::getGPIO retrieves the current level of the GPIO pins
 * @param mask          Bit mask of the current level
 * @return Success of the operation
 */
bool QrfeProtocolHandler::getGPIO ( ulong &mask )
{
    Global::trc(1, "Get GPIO - Trying to get GPIO Value");

    uchar com1 = RFE_COM1_READER_GPIO;
    uchar com2 = RFE_COM2_GET_GPIO;

    QByteArray response;
    bool res = send2ReaderWaitForResponse(com1, com2, m_responseTimeOut, "Get GPIO", response);

    if(res != true)
        return res;

    if (response.size() != 4){
        Global::trc(0, "Get GPIO - NOK - Payl");
        Global::trc(0, " Recvd: " + response.toHex());

        return false;
    }

    mask = 0;
    mask |= (((ulong) (uchar) response.at(0)) << 24);
    mask |= (((ulong) (uchar) response.at(1)) << 16);
    mask |= (((ulong) (uchar) response.at(2)) << 8);
    mask |= (ulong) (uchar) response.at(3);

    Global::trc(1, "Get GPIO Value - OK : Mask = " + QString("%1").arg(mask, 8, 16, QChar('0')) );

    return true;

}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

/**
 * @brief QrfeProtocolHandler::doSingleInventory executes a single inventory at the reader.
 * @param epc   List of found tags
 * @return Success of the operation
 */
bool QrfeProtocolHandler::doSingleInventory(QList<QrfeTagEvent> &tagList)
{
    Global::trc(1, "Single Inventory - Trying to do an inventory");

    uchar com1 = RFE_COM1_TAG_FUNCTIONS;
    uchar com2 = RFE_COM2_INVENTORY_SINGLE;

    QByteArray response;
    bool res = send2Reader(com1, com2);

    if(res != true)
        return res;

    tagList.clear();
    uchar epcsOverall = 0;
    uchar epcsReceived = 0;

    while(1)
    {
        bool res = waitForResponse(com1, com2, m_responseTimeOut, "Single Inventory", response);

        if(!res)
            return res;

        if (response.size() == 0 || response.at(0) != RFE_RET_SUCCESS)
        {
            Global::trc(0, "Single Inventory - NOK - Payl");
            Global::trc(0, " Recvd: " + response.toHex());
            m_lastReturnCode = (eRFE_RET_VALUE) (uchar) response.at(0);
            return false;
        }

        epcsOverall = response.at(1);
        uchar epcsInMessage = response.at(2);
        uchar index = 3;

        Global::trc(0, " " + QString::number(epcsInMessage) + "/" + QString::number(epcsOverall));

        for (int i = 0; i < epcsInMessage; i++)
        {
            QByteArray tagEventPayload = response.mid(index+1, (uchar)response.at(index));
            bool ok = false;
            QrfeTagEvent tagEvent = parseTagEvent(tagEventPayload, ok);
            Global::trc(0, "    " + QString::number(i) + " " + QString::number(tagEvent.tagId.size()) + "> " + tagEvent.tagId.toHex());

            tagList << tagEvent;

            index += (uchar)response.at(index);
            index++;
        }
        epcsReceived += epcsInMessage;

        if(epcsReceived >= epcsOverall)
            break;
    }

    Global::trc(1, "Single Inventory - OK");

    return true;
}

/**
 * @brief QrfeProtocolHandler::setCyclicInventory sets the cyclic inventory on or off
 * @param on    Cyclic inventory mode
 * @param time  Specified time
 * @return Success of the operation
 */
bool QrfeProtocolHandler::setCyclicInventory(bool on, ulong time)
{
    Global::trc(1, "Cyclic Inventory - Trying to set cyclic inventory to " + QString::number(on));

    QByteArray payload;
    payload.resize(1);
    if (on){
        payload[0] = INVENTORY_ON;
        if(time != 0)
        {
            payload.append((time >> 24));
            payload.append((time >> 16));
            payload.append((time >> 8));
            payload.append((time));
        }
    }
    else{
        payload[0] = INVENTORY_OFF;
    }

    uchar com1 = RFE_COM1_TAG_FUNCTIONS;
    uchar com2 = RFE_COM2_INVENTORY_CYCLIC;

    QByteArray response;
    bool res = send2ReaderWaitForResponse(com1, com2, payload, m_responseTimeOut, "Cyclic Inventory", response);

    if(res != true)
        return res;

    // set the variable to the new value
    if (response.size() == 0 || response.at(0) != RFE_RET_SUCCESS)
    {
        Global::trc(0, "Cyclic Inventory - NOK - Payl");
        Global::trc(0, " Recvd: " + response.toHex());

        m_lastReturnCode = (eRFE_RET_VALUE) (uchar) response.at(0);
        return false;
    }

    Global::trc(1, "Cyclic Inventory - OK");

    return true;
}

/**
 * @brief QrfeProtocolHandler::readFromTag reads data from a tag.
 * @param epc       EPC of the specified tag
 * @param mem_bank  Memory bank where to read data from
 * @param address   Address within the memory bank
 * @param passwd    The access password to read from the tag
 * @param count     The count of data that should be read
 * @param data      The read data
 * @return Success of the operation
 */
bool QrfeProtocolHandler::readFromTag(QByteArray epc,
        uchar mem_bank, ushort address, QByteArray passwd, uchar count,
        QByteArray &data)
{
    Global::trc(1, "Read From Tag - Trying to read from tag " + epc.toHex() + " from memory bank "
            + QString::number(mem_bank) + " and address " + QString::number(
            address) + " the count " + QString::number(count));

    if (passwd.size() != 4){
        Global::trc(0, "Read From Tag - NOK - Data");
        return false;
    }


    QByteArray payload;
    payload.append(epc.size());
    payload.append(epc);

    payload.append((char) mem_bank);
    payload.append((char) (address >> 8));
    payload.append((char) address);
    payload.append(passwd);

    payload.append(count);

    uchar com1 = RFE_COM1_TAG_FUNCTIONS;
    uchar com2 = RFE_COM2_READ_FROM_TAG;

    QByteArray response;
    bool res = send2ReaderWaitForResponse(com1, com2, payload, m_responseTimeOut * 4, "Read From Tag", response);

    if(res != true)
        return res;

    // set the variable to the new value
    if (response.size() == 0 || (response.at(0) != RFE_RET_SUCCESS && response.at(0) != RFE_RET_RESULT_PENDING) )
    {
        Global::trc(0, "Read From Tag - NOK - Payl");
        Global::trc(0, " Recvd: " + response.toHex());

        m_lastReturnCode = (eRFE_RET_VALUE) (uchar) response.at(0);
        return false;
    }

    if(response.at(0) == RFE_RET_SUCCESS)
    {
        data = response.mid(2, (uchar)response.at(1));
    }
    else if(response.at(0) == RFE_RET_RESULT_PENDING)
    {
        Global::trc(0, "Read From Tag - OK - Result Pending");

        if (response.size() != 2)
        {
            Global::trc(0, "Read From Tag - NOK - Payl");
            Global::trc(0, " Recvd: " + response.toHex());

            m_lastReturnCode = (eRFE_RET_VALUE) (uchar) response.at(0);
            return false;
        }

        QByteArray pendingResp;
        if(!waitForPendingMessage((uchar)response.at(1), m_pendingResponseTimeOut, "Read From Tag", pendingResp) ){
            Global::trc(0, " Recvd: " + response.toHex());
            return false;
        }

        // set the variable to the new value
        if (pendingResp.size() == 0 || pendingResp.at(0) != RFE_RET_SUCCESS )
        {
            Global::trc(0, "Read From Tag - NOK - Payl");
            Global::trc(0, " Recvd: " + response.toHex() + " | Recvd: " + pendingResp.toHex());
            m_lastReturnCode = (eRFE_RET_VALUE) (uchar) pendingResp.at(0);
            return false;
        }

        data = pendingResp.mid(2, (uchar)pendingResp.at(1));
    }


    Global::trc(1, "Read From Tag - OK : Read the data from the tag: " + data.toHex());

    return true;
}

/**
 * @brief QrfeProtocolHandler::writeToTag writes data to the a tag.
 * @param epc       EPC of the specified tag
 * @param mem_bank  Memory bank where data should be written to
 * @param address   Address within the memory bank
 * @param passwd    The access password to write to the tag
 * @param data      The data that should be written
 * @return Success of the operation
 */
bool QrfeProtocolHandler::writeToTag(QByteArray epc,
        uchar mem_bank, ushort address, QByteArray passwd, QByteArray data)
{
    Global::trc(1, "Write To Tag - Trying to write to tag " + epc.toHex() + " at bank "
            + QString::number(mem_bank) + " and address " + QString::number(
            address) + " the bytes " + data.toHex());

    if (passwd.size() != 4){
        Global::trc(1, "Write To Tag - NOK - Data");
        return false;
    }

    QByteArray payload;
    payload.append(epc.size());
    payload.append(epc);

    payload.append((char) mem_bank);
    payload.append((char) (address >> 8));
    payload.append((char) address);
    payload.append(passwd);

    payload.append(data.size());
    payload.append(data);

    uchar com1 = RFE_COM1_TAG_FUNCTIONS;
    uchar com2 = RFE_COM2_WRITE_TO_TAG;

    QByteArray response;
    bool res = send2ReaderWaitForResponse(com1, com2, payload, m_responseTimeOut * 4, "Write To Tag", response);

    if(res != true)
        return res;

    // set the variable to the new value
    if (response.size() == 0 || (response.at(0) != RFE_RET_SUCCESS && response.at(0) != RFE_RET_RESULT_PENDING) )
    {
        Global::trc(0, "Write To Tag - NOK - Payl");
        Global::trc(0, " Recvd: " + response.toHex());

        m_lastReturnCode = (eRFE_RET_VALUE) (uchar) response.at(0);
        return false;
    }

    if(response.at(0) == RFE_RET_RESULT_PENDING)
    {
        Global::trc(0, "Write To Tag - OK - Result Pending");

        if (response.size() != 2)
        {
            Global::trc(0, "Write To Tag - NOK - Payl");
            Global::trc(0, " Recvd: " + response.toHex());

            m_lastReturnCode = (eRFE_RET_VALUE) (uchar) response.at(0);
            return false;
        }

        QByteArray pendingResp;
        if(!waitForPendingMessage((uchar)response.at(1), m_pendingResponseTimeOut, "Write To Tag", pendingResp) ){
            Global::trc(0, " Recvd: " + response.toHex());
            return false;
        }

        // set the variable to the new value
        if (pendingResp.size() == 0 || pendingResp.at(0) != RFE_RET_SUCCESS )
        {
            Global::trc(0, "Write To Tag - NOK - Payl");
            Global::trc(0, " Recvd: " + response.toHex() + " | Recvd: " + pendingResp.toHex());
            m_lastReturnCode = (eRFE_RET_VALUE) (uchar) pendingResp.at(0);
            return false;
        }
    }

    Global::trc(1, "Write To Tag - OK");

    return true;
}

/**
 * @brief QrfeProtocolHandler::lockTag locks a specfied memory region of a tag.
 * @param epc       EPC of the specified tag
 * @param mode      The lock mode
 * @param memory    The memory region
 * @param password  The access password to lock the tag
 * @return Success of the operation
 */
bool QrfeProtocolHandler::lockTag(QByteArray epc, uchar mode,
        uchar memory, QByteArray password)
{
    Global::trc(1, "Lock Tag - Trying to lock tag " + epc.toHex() + " with the mode "
            + QString::number(mode) + " the memory " + QString::number(memory));

    if (password.size() != 4){
        Global::trc(0, "Lock Tag - NOK - Data");
        return false;
    }

    QByteArray payload;
    payload.append(epc.size());
    payload.append(epc);

    payload.append((char) mode);
    payload.append((char) memory);
    payload.append(password);

    uchar com1 = RFE_COM1_TAG_FUNCTIONS;
    uchar com2 = RFE_COM2_LOCK_TAG;

    QByteArray response;
    bool res = send2ReaderWaitForResponse(com1, com2, payload, m_responseTimeOut * 2, "Lock Tag", response);

    if(res != true)
        return res;

    // set the variable to the new value
    if (response.size() == 0 || response.at(0) != RFE_RET_SUCCESS)
    {
        Global::trc(0, "Lock Tag - NOK - Payl");
        Global::trc(0, " Recvd: " + response.toHex());

        m_lastReturnCode = (eRFE_RET_VALUE) (uchar) response.at(0);
        return false;
    }

    Global::trc(1, "Lock Tag - OK");

    return true;
}

/**
 * @brief QrfeProtocolHandler::killTag kills a tag
 * @param epc       EPC of the specified tag
 * @param rfu       rfu
 * @param recom     recom
 * @param password  The kill password to kill the tag
 * @return Success of the operation
 */
bool QrfeProtocolHandler::killTag(QByteArray epc, uchar rfu,
        uchar recom, QByteArray password)
{
    Global::trc(1, "Kill Tag - Trying to kill tag " + epc.toHex() + " with the rfu "
            + QString::number(rfu) + " the recom " + QString::number(recom));

    if (password.size() != 4){
        Global::trc(0, "Kill Tag - NOK - Data");
        return false;
    }

    QByteArray payload;
    payload.append(epc.size());
    payload.append(epc);

    payload.append(((rfu & 0x0F) << 4) | (recom & 0x0F));
    payload.append(password);

    uchar com1 = RFE_COM1_TAG_FUNCTIONS;
    uchar com2 = RFE_COM2_KILL_TAG;

    QByteArray response;
    bool res = send2ReaderWaitForResponse(com1, com2, payload, m_responseTimeOut * 2, "Kill Tag", response);

    if(res != true)
        return res;

    // set the variable to the new value
    if (response.size() == 0 || response.at(0) != RFE_RET_SUCCESS)
    {
        Global::trc(0, "Kill Tag - NOK - Payl");
        Global::trc(0, " Recvd: " + response.toHex());

        m_lastReturnCode = (eRFE_RET_VALUE) (uchar) response.at(0);
        return false;
    }

    Global::trc(1, "Kill Tag - OK");

    return true;

}


///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
 * @brief QrfeProtocolHandler::send2Reader sends command without payload to reader
 * @param com1      Command #1
 * @param com2      Command #2
 * @return Success of the operation
 */
bool QrfeProtocolHandler::send2Reader(uchar com1, uchar com2)
{
    return send2Reader(com1, com2, QByteArray());
}

/**
 * @brief QrfeProtocolHandler::send2Reader sends command with payload to reader
 * @param com1      Command #1
 * @param com2      Command #2
 * @param payload   Payload of the command
 * @return Success of the operation
 */
bool QrfeProtocolHandler::send2Reader(uchar com1, uchar com2, const QByteArray& payload)
{
    m_messageQueue.clearMessage(messageId(com1, com2));

    QByteArray msg;
    msg.resize(8);
    msg[0] = RFE_START_BYTE_1;
    msg[1] = RFE_START_BYTE_2;
    msg[2] = RFE_START_BYTE_3;
    msg[3] = RFE_COMMAND_START_BYTE;
    msg[4] = com1;
    msg[5] = com2;
    msg[6] = RFE_LENGTH_START_BYTE;
    msg[7] = payload.size();

    if (payload.size() > 0)
    {
        msg.append(RFE_PAYLOAD_START_BYTE);
        msg.append(payload);
    }

    msg.append(RFE_CHECKSUM_START_BYTE);
    msg.append(calcXORCS(msg));

    Global::trc(5, "Sending: " + msg.toHex());

    if(m_sendBuffer->write(msg) != msg.size())
        return false;

    return true;
}

/**
 * @brief QrfeProtocolHandler::send2ReaderWaitForResponse waits for a specific response
 * @param com1      Command #1
 * @param com2      Command #2
 * @param waitTime  Maximum wait time
 * @param funcName  Name of the function, for trace
 * @param response  The received response
 * @return Success of the operation
 */
bool QrfeProtocolHandler::waitForResponse(uchar com1, uchar com2, uint waitTime, const QString& funcName, QByteArray& response)
{
    // reset the flag
    m_lastReturnCode = RFE_RET_SUCCESS;

    // wait for either the flag or a timeout
    bool result = false;
    response = m_messageQueue.waitForMessage(messageId(com1, com2), waitTime, &result);
    if (!result){
        Global::trc(0, funcName + " - NOK - Resp");
        return false;
    }

    return true;
}

/**
 * @brief QrfeProtocolHandler::send2ReaderWaitForResponse waits for a specific response
 * @param com1      Command #1
 * @param com2      Command #2
 * @param waitTime  Maximum wait time
 * @param funcName  Name of the function, for trace
 * @param response  The received response
 * @return Success of the operation
 */
bool QrfeProtocolHandler::waitForPendingMessage(uchar id, uint waitTime, const QString& funcName, QByteArray& response)
{
    // reset the flag
    m_lastReturnCode = RFE_RET_SUCCESS;

    // wait for either the flag or a timeout
    bool result = false;
    response = m_pendingResultsQueue.waitForMessage(id, waitTime, &result);
    if (!result){
        Global::trc(0, funcName + " - NOK - Pending Resp");
        return false;
    }

    return true;
}


/**
 * @brief QrfeProtocolHandler::send2ReaderWaitForResponse sends command without payload to reader and waits for response
 * @param com1      Command #1
 * @param com2      Command #2
 * @param waitTime  Maximum wait time
 * @param funcName  Name of the function, for trace
 * @param response  The received response
 * @return Success of the operation
 */
bool QrfeProtocolHandler::send2ReaderWaitForResponse(uchar com1, uchar com2, uint waitTime, const QString& funcName, QByteArray& response)
{
    return send2ReaderWaitForResponse(com1, com2, QByteArray(), waitTime, funcName, response);
}

/**
 * @brief QrfeProtocolHandler::send2ReaderWaitForResponse sends command with payload to reader and waits for response
 * @param com1      Command #1
 * @param com2      Command #2
 * @param payload   Payload of the command
 * @param waitTime  Maximum wait time
 * @param funcName  Name of the function, for trace
 * @param response  The received response
 * @return Success of the operation
 */
bool QrfeProtocolHandler::send2ReaderWaitForResponse(uchar com1, uchar com2, const QByteArray& payload, uint waitTime, const QString& funcName, QByteArray& response)
{
    // reset the flag
    m_lastReturnCode = RFE_RET_SUCCESS;

    // send the command
    bool res = send2Reader(com1, com2, payload);
    if (res != true)
    {
        Global::trc(0, funcName + " - NOK - Send");
        return res;
    }

    // wait for either the flag or a timeout
    bool result = false;
    response = m_messageQueue.waitForMessage(messageId(com1, com2), waitTime, &result);
    if (!result){
        Global::trc(0, funcName + " - NOK - Resp");
        return false;
    }

    return true;
}

/**
 * @brief QrfeProtocolHandler::send2ReaderWithoutResponse sends command without payload to reader and does not wait for response
 * @param com1      Command #1
 * @param com2      Command #2
 * @param funcName  Name of the function, for trace
 * @return Success of the operation
 */
bool QrfeProtocolHandler::send2ReaderWithoutResponse(uchar com1, uchar com2, const QString& funcName)
{
    return send2ReaderWithoutResponse(com1, com2, QByteArray(), funcName);
}

/**
 * @brief QrfeProtocolHandler::send2ReaderWithoutResponse sends command with payload to reader and does not wait for response
 * @param com1      Command #1
 * @param com2      Command #2
 * @param payload   Payload of the command
 * @param funcName  Name of the function, for trace
 * @return Success of the operation
 */
bool QrfeProtocolHandler::send2ReaderWithoutResponse(uchar com1, uchar com2, const QByteArray& payload, const QString& funcName)
{
    // reset the flag
    m_lastReturnCode = RFE_RET_SUCCESS;

    // send the command
    bool res = send2Reader(com1, com2, payload);
    if (res != true)
    {
        Global::trc(0, funcName + " - NOK - Send");
        return res;
    }

    return true;
}


/**
 * @brief QrfeProtocolHandler::heartBeatISR parses a heartbeat interrupt
 * @param payload Payload to parse
 */
void QrfeProtocolHandler::heartBeatISR(const QByteArray& payload)
{
    if (payload.size() != 1 || payload.at(0) != RFE_RET_SUCCESS)
        return;

    QMetaObject::invokeMethod(this, "_q_emit_heartBeat", Qt::QueuedConnection);
}

/**
 * @brief QrfeProtocolHandler::cyclicInventoryISR parses a cyclic inventory interrupt
 * @param payload Payload to parse
 */
void QrfeProtocolHandler::cyclicInventoryISR(const QByteArray& payload)
{
    bool ok = false;
    QrfeTagEvent event = parseTagEvent(payload, ok);
    if(ok)
        QMetaObject::invokeMethod(this, "_q_emit_cyclicInventory", Qt::QueuedConnection, Q_ARG(const QrfeTagEvent &, event));
}

/**
 * @brief QrfeProtocolHandler::stateChangedISR parses a state changed interrupt
 * @param payload Payload to parse
 */
void QrfeProtocolHandler::stateChangedISR(const QByteArray& payload)
{
    if (payload.size() != 1)
        return;

    int state = payload.at(0);

    QMetaObject::invokeMethod(this, "_q_emit_stateChanged", Qt::QueuedConnection, Q_ARG(const int, state));
}

/**
 * @brief QrfeProtocolHandler::statusRegChangedISR parses a status register changed interrupt
 * @param payload Payload to parse
 */
void QrfeProtocolHandler::statusRegChangedISR ( const QByteArray& payload )
{
    if (payload.size() != 8)
        return;

    qulonglong statusRegister = 0;
    statusRegister |= (((qulonglong) (uchar) payload.at(0)) << 56);
    statusRegister |= (((qulonglong) (uchar) payload.at(1)) << 48);
    statusRegister |= (((qulonglong) (uchar) payload.at(2)) << 40);
    statusRegister |= (((qulonglong) (uchar) payload.at(3)) << 32);
    statusRegister |= (((qulonglong) (uchar) payload.at(4)) << 24);
    statusRegister |= (((qulonglong) (uchar) payload.at(5)) << 16);
    statusRegister |= (((qulonglong) (uchar) payload.at(6)) << 8);
    statusRegister |= (qulonglong) (uchar) payload.at(7);

    QMetaObject::invokeMethod(this, "_q_emit_statusRegisterChanged", Qt::QueuedConnection, Q_ARG(const qulonglong, statusRegister));
}

/**
 * @brief QrfeProtocolHandler::gpioValuesChangedISR parses a gpio values changed interrupt
 * @param payload   Payload to parse
 */
void QrfeProtocolHandler::gpioValuesChangedISR ( const QByteArray& payload )
{
    if (payload.size() != 4)
        return;

    ulong gpioValues = 0;
    gpioValues |= (((ulong) (uchar) payload.at(0)) << 24);
    gpioValues |= (((ulong) (uchar) payload.at(1)) << 16);
    gpioValues |= (((ulong) (uchar) payload.at(2)) << 8);
    gpioValues |= (ulong) (uchar) payload.at(3);

    QMetaObject::invokeMethod(this, "_q_emit_gpioValuesChanged", Qt::QueuedConnection, Q_ARG(const ulong, gpioValues));
}

/**
 * @brief QrfeProtocolHandler::operationResultISR parses a operation result interrupt
 * @param payload   Payload to parse
 */
void QrfeProtocolHandler::operationResultISR ( const QByteArray& payload )
{
    Global::trc(1, "Operation Result");

    if (payload.size() < 2)
            return;

    uchar id = (uchar)payload.at(0);
    Global::trc(1, "Operation Result for id " + QString::number(id));

    m_pendingResultsQueue.enqueueMessage(id, payload.right(payload.size()-1));
}


/**
 * @brief QrfeProtocolHandler::_q_emit_heartBeat is a function to emit heartBeat signal
 */
void QrfeProtocolHandler::_q_emit_heartBeat()
{
    emit heartBeat();
}

/**
 * @brief QrfeProtocolHandler::_q_emit_cyclicInventory is a function to emit cyclicInventory signal
 * @param tagInfo   Received tag info
 */
void QrfeProtocolHandler::_q_emit_cyclicInventory(const QrfeTagEvent &tagInfo)
{
    emit cyclicInventory(tagInfo);
}

/**
 * @brief QrfeProtocolHandler::_q_emit_stateChanged is a function to emit stateChanged signal
 * @param newState  New state
 */
void QrfeProtocolHandler::_q_emit_stateChanged(const int newState)
{
    emit stateChanged(newState);
}

/**
 * @brief QrfeProtocolHandler::_q_emit_statusRegisterChanged is a function to emit statusRegisterChanged signal
 * @param statusRegister    New status register
 */
void QrfeProtocolHandler::_q_emit_statusRegisterChanged(const qulonglong statusRegister)
{
    emit statusRegisterChanged(statusRegister);
}

/**
 * @brief QrfeProtocolHandler::_q_emit_gpioValuesChanged is a function to emit gpioValuesChanged signal
 * @param gpioValues        New GPIO values
 */
void QrfeProtocolHandler::_q_emit_gpioValuesChanged( const ulong gpioValues )
{
    emit gpioValuesChanged(gpioValues);
}


/**
 * @brief QrfeProtocolHandler::readFromDevice parses the received bytes from the device and splits it up to sngle messages.
 */
void QrfeProtocolHandler::readFromDevice()
{
    QByteArray data = m_device->readAll();

    Global::trc(10, "-> RawMessage: " + data.toHex());

    foreach (uchar c , data)
    {
        switch (m_state)
        {
            case START_BYTE_1:
                if (c == RFE_START_BYTE_1)
                {
                    m_state = START_BYTE_2;
                    m_singleMsg.clear();
                    m_payloadIndex = 0;
                    m_payloadLength = 0;
                }
                break;

            case START_BYTE_2:
                if (c == RFE_START_BYTE_2)
                    m_state = START_BYTE_3;
                else
                    m_state = START_BYTE_1;
                break;

            case START_BYTE_3:
                if (c == RFE_START_BYTE_3)
                    m_state = COMMAND_START_BYTE;
                else
                    m_state = START_BYTE_1;
                break;

            case COMMAND_START_BYTE:
                if (c == RFE_COMMAND_START_BYTE)
                    m_state = COMMAND_1;
                else
                    m_state = START_BYTE_1;
                break;

            case COMMAND_1:
                m_state = COMMAND_2;
                break;

            case COMMAND_2:
                m_state = LENGTH_START_BYTE;
                break;

            case LENGTH_START_BYTE:
                if (c == RFE_LENGTH_START_BYTE)
                    m_state = LENGTH;
                else
                    m_state = START_BYTE_1;
                break;

            case LENGTH:
                m_payloadLength = c;
                m_payloadIndex = 0;
                if (m_payloadLength == 0)
                    m_state = CHECKSUM_START_BYTE;
                else
                    m_state = PAYLOAD_START_BYTE;
                break;

            case PAYLOAD_START_BYTE:
                if (c == RFE_PAYLOAD_START_BYTE)
                    m_state = PAYLOAD;
                else
                    m_state = START_BYTE_1;
                break;

            case PAYLOAD:
                if (++m_payloadIndex >= m_payloadLength)
                    m_state = CHECKSUM_START_BYTE;

                break;

            case CHECKSUM_START_BYTE:
                if (c == RFE_CHECKSUM_START_BYTE)
                    m_state = CHECKSUM;
                else
                    m_state = START_BYTE_1;
                break;

            case CHECKSUM:
                {
                    m_state = START_BYTE_1;
                    if (c != calcXORCS(m_singleMsg))
                    {
                        Global::trc(0, "CHECKSUM NOK!!");
                        break;
                    }

                    Global::trc(9, "-> SingleMessage: " + m_singleMsg.toHex());

                    computeMessage(m_singleMsg);

                    break;
                }

        }
        m_singleMsg.append(c);
    }

}

/**
 * @brief QrfeProtocolHandler::sendToDevice sends data from send buffer to device
 */
void QrfeProtocolHandler::sendToDevice()
{
    if(!m_device->isOpen())
        Global::trc(0, "Trying to write but device is not open!");

    QByteArray data = m_sendBuffer->readAll();
    m_device->write(data);

    Global::trc(6, "Sent " + data.toHex());
}

/**
 * @brief QrfeProtocolHandler::computeMessage computes a single message and stores it in the message queue or computes it.
 * @param msg   A single message from the reader
 */
void QrfeProtocolHandler::computeMessage(const QByteArray &  msg)
{
    uchar command1 = msg[RFE_COMMAND_INDEX_1];
    uchar command2 = msg[RFE_COMMAND_INDEX_2];

    QByteArray payload = msg.mid(RFE_PAYLOAD_INDEX, msg[RFE_LENGTH_INDEX]);

    switch (command1)
    {
        case RFE_COM1_INTERRUPT: // Interrupts
            switch (command2)
            {
                case RFE_COM2_HEARTBEAT_INTERRUPT:
                    heartBeatISR(payload);
                    break;
                case RFE_COM2_INVENTORY_CYCLIC_INTERRUPT:
                    cyclicInventoryISR(payload);
                    break;
                case RFE_COM2_STATE_CHANGED_INTERRUPT:
                    stateChangedISR(payload);
                    break;
                case RFE_COM2_STATUS_REG_CHANGED_INTERRUPT:
                    statusRegChangedISR(payload);
                    break;
                case RFE_COM2_OPERATION_RESULT_INTERRUPT:
                    operationResultISR(payload);
                    break;
                case RFE_COM2_GPIO_PINS_CHANGED:
                    gpioValuesChangedISR(payload);
                    break;
                default:
                    m_messageQueue.enqueueMessage(messageId(command1, command2), payload);
                    break;
            }
            break;
        default:
            m_messageQueue.enqueueMessage(messageId(command1, command2), payload);
            break;
    }
}

/**
 * @brief QrfeProtocolHandler::messageId generates a unique message id for the command bytes
 * @param command1  Command byte 1
 * @param command2  Command byte 2
 * @return Unique message id
 */
int QrfeProtocolHandler::messageId(uchar command1, uchar command2)
{
    return (((int)command1) << 8) | command2;
}

/**
 * @brief QrfeProtocolHandler::parseTagEvent parses a given payload part for tag info
 * @param payload   Given payload part
 * @param ok        Returns if parsing was ok
 * @return  The parsed tag event
 */
QrfeTagEvent QrfeProtocolHandler::parseTagEvent(const QByteArray& payload, bool& ok)
{
    typedef enum
    {
        START,
        TAGID_LENGTH,
        TAGID,
        RSSI,
        MEM_BANK,
        MEM_ADDR1,
        MEM_ADDR2,
        MEM_SIZE,
        MEM_DATA,
        TRIGGER,
        ANTENNA,
        FREQUENCY,
        HANDLE,
        STATE,
        BATTERY,
        PC,
        MESSAGE_ID,
        APPLICATION_SIZE,
        APPLICATION_DATA
    } INVENTORY_STATE;

    INVENTORY_STATE state = START;

    QrfeTagEvent 	event;

    uchar tagIdLength = 0;
    uchar mem_size = 0;
    QByteArray frequency_data;
    QByteArray state_data;
    uchar applInfoSize = 0;


    ok = false;

    for (int i = 0; i < payload.size(); i++)
    {
        uchar c = payload.at(i);
        switch (state)
        {
        case START:
            if (c == RFE_TAG_ID_START_BYTE)
                state = TAGID_LENGTH;
            else if (c == RFE_RSSI_START_BYTE)
                state = RSSI;
            else if (c == RFE_MEM_START_BYTE)
                state = MEM_BANK;
            else if (c == RFE_TRIGGER_START_BYTE)
                state = TRIGGER;
            else if (c == RFE_ANTENNA_ID_START_BYTE)
                state = ANTENNA;
            else if (c == RFE_READ_FREQU_START_BYTE)
                state = FREQUENCY;
            else if (c == RFE_GEN2_HANDLE_START_BYTE)
                state = HANDLE;
            else if (c == RFE_STATE_START_BYTE)
                state = STATE;
            else if (c == RFE_BATTERY_START_BYTE)
                state = BATTERY;
            else if (c == RFE_GEN2_PC_START_BYTE)
                state = PC;
            else if (c == RFE_MESSAGE_ID_START_BYTE)
                state = MESSAGE_ID;
            else if (c == RFE_APPLICATION_START_BYTE)
                state = APPLICATION_SIZE;
            else
                state = START;
            break;

        case TAGID_LENGTH:
            tagIdLength = c;
            if(tagIdLength > 0)
                state = TAGID;
            else
                state = START;
            break;

        case TAGID:
            event.tagId.append(c);
            if (event.tagId.size() == tagIdLength)
                state = START;
            break;

        case RSSI:
            event.rssi.append(c);
            if (event.rssi.size() == 2){
                state = START;
                event.hasRSSI = true;
            }
            break;

        case MEM_BANK:
            event.memBank = c;
            state = MEM_ADDR1;
            break;

        case MEM_ADDR1:
            event.memAddr = 0;
            event.memAddr += ((ushort) c) << 8;
            state = MEM_ADDR2;
            break;

        case MEM_ADDR2:
            event.memAddr += c;
            state = MEM_SIZE;
            break;

        case MEM_SIZE:
            mem_size = c;
            state = MEM_DATA;
            break;

        case MEM_DATA:
            event.memData.append(c);
            if (event.memData.size() == mem_size){
                state = START;
                event.hasMemory = true;
            }
            break;

        case TRIGGER:
            event.trigger = (eRFE_TRIGGER_SOURCE) c;
            event.hasTrigger = true;
            state = START;
            break;

        case ANTENNA:
            event.antennaId = c;
            event.hasAntenna = true;
            state = START;
            break;

        case FREQUENCY:
            frequency_data.append(c);
            if (frequency_data.size() == 3){
                event.readFrequency = 0;
                event.readFrequency |= (((ulong) (uchar) frequency_data.at(0)) << 16);
                event.readFrequency |= (((ulong) (uchar) frequency_data.at(1)) << 8);
                event.readFrequency |=   (ulong) (uchar) frequency_data.at(2);
                event.hasReadFrequency = true;
                state = START;
            }
            break;

        case HANDLE:
            event.handle.append(c);
            if (event.handle.size() == 2){
                state = START;
                event.hasHandle = true;
            }
            break;

        case STATE:
            state_data.append(c);
            if (state_data.size() == 2){
                event.state = 0;
                event.state |= (((ushort) (uchar) state_data.at(0)) << 8);
                event.state |=   (ushort) (uchar) state_data.at(1);
                state = START;
                event.hasState = true;
            }
            break;

        case BATTERY:
            event.battery = c;
            event.hasBattery = true;
            state = START;
            break;

        case PC:
            event.pc.append(c);
            if (event.pc.size() == 2){
                state = START;
                event.hasPC = true;
            }
            break;

        case MESSAGE_ID:
            event.messageID = c;
            event.hasMessageID = true;
            state = START;
            break;

        case APPLICATION_SIZE:
            event.hasApplicationInfo = true;
            applInfoSize = c;
            state = APPLICATION_DATA;
            break;

        case APPLICATION_DATA:
            event.applicationInfo.append(c);
            if(event.applicationInfo.size() == applInfoSize)
            {
                event.hasApplicationInfo = true;
                state = START;
            }
            state = APPLICATION_DATA;
            break;
        }

    }

    if (event.tagId.size() != tagIdLength)
        return event;

    ok = true;

    return event;
}

/**
 * @brief QrfeProtocolHandler::calcXORCS cclulates the XOR checksum for the given data.
 * @param data  The data to calc checksum
 * @return The XOR checksum
 */
uchar QrfeProtocolHandler::calcXORCS(const QByteArray &  data)
{
    uchar result = 0;
    foreach (uchar c, data)
    {
        result ^= c;
    }
    return result;
}

/**
 * @brief QrfeProtocolHandler::run implements the thread
 */
void QrfeProtocolHandler::run()
{
    m_sendBuffer = new QrfeFifo();
    m_sendBuffer->open(QIODevice::ReadWrite);

    // connect to the ready read signal of the device
    connect(m_sendBuffer, 		SIGNAL(readyRead()),
            this, 				  SLOT(sendToDevice()), Qt::DirectConnection);
    connect(m_device, 			SIGNAL(readyRead()),
            this, 				  SLOT(readFromDevice()), Qt::DirectConnection);

    Global::trc(2, "Worker thread initialized");

    m_threadInitialized = true;

    exec();

    if(m_device->isOpen())
        m_device->close();

    if(m_sendBuffer && m_sendBuffer->isOpen())
        m_sendBuffer->close();

    delete m_sendBuffer;
    delete m_device;
}
